package ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;

import color.CLRConvert;

import delaunay_triangulation.Point_dt;
import delaunay_triangulation.Triangle_dt;

import triangulation.DelaunayMapping;
import triangulation.SimpleTriangulation;
import util.GeoEdge;
import util.GeoVertex;

public class Triangulation2DFrame extends JFrame
{
	private final int MOVE_SPD = 2;
	private final int ZOOM_SPD = 1;
	private final int ACCEL_SPD = 3;
	
	// class that actually handles the drawing of the 2D triangulation
	class DrawPane extends JPanel
	{
		public SimpleTriangulation triangulation;
		
		public int nPixelSize = 10; // spacing between each vertex in the triangulation
		public int nOffsetX = UIConst.APP_WIDTH/2;
		public int nOffsetY = UIConst.APP_HEIGHT/2;
		
		private Random rand;
		protected GeoVertex src;
		protected boolean bShowNRing;
		protected boolean bShowEdgeVertexSet;
		protected boolean bShowEdgeSet;
		
		protected void selectedRandomVertex() { src = triangulation.selectRandomVertex(rand); }
		
		public DrawPane()
		{
			setupGUI();
			rand =  new Random();
			src = null;
			bShowNRing = false;
			bShowEdgeVertexSet = false;
			bShowEdgeSet = false;
		}
		
		public void setupGUI()
		{
			setLayout(new BorderLayout(2,2));
			setSize(UIConst.APP_WIDTH, UIConst.APP_HEIGHT);
			setBackground(Color.WHITE);
			
//			createOptionsPanel();
		}
		
		public void createOptionsPanel()
		{
			JPanel optionsPanel = new JPanel();
			btnSelectRandomVertex = new JButton("Random Vertex");
			btnSelectRandomVertex.addActionListener(new ActionListener()
			{
				public void actionPerformed(ActionEvent e)
				{
					canvas.selectedRandomVertex();
				}
			});
			
			cbShowNRing = new JCheckBox("Show 9-Ring");
			cbShowEdgeVertexSet = new JCheckBox("Show Edge Vertex Set");
			cbShowEdgeSet = new JCheckBox("Show Edge Set");
			
			cbShowNRing.addActionListener(new ActionListener()
			{
				public void actionPerformed(ActionEvent e)
				{
					canvas.bShowNRing = true;
				}
			});
			
			cbShowEdgeVertexSet.addActionListener(new ActionListener()
			{
				public void actionPerformed(ActionEvent e)
				{
					canvas.bShowEdgeVertexSet = true;
				}
			});
			
			cbShowEdgeSet.addActionListener(new ActionListener()
			{
				public void actionPerformed(ActionEvent e)
				{
					canvas.bShowEdgeSet = true;
				}
			});
			
			ButtonGroup displayGroup = new ButtonGroup();
			displayGroup.add(cbShowNRing);
			displayGroup.add(cbShowEdgeVertexSet);
			displayGroup.add(cbShowEdgeSet);
			
			optionsPanel.add(btnSelectRandomVertex);
			optionsPanel.add(cbShowNRing);
			optionsPanel.add(cbShowEdgeVertexSet);
			optionsPanel.add(cbShowEdgeSet);

			add(optionsPanel, BorderLayout.SOUTH);
			
		}
		
		public void setTriangulation(SimpleTriangulation t)
		{
			triangulation = t;
		}
		
		public void DrawTriangulation(Graphics g)
		{
			g.setColor(Color.BLACK);
			
    		Iterator<Triangle_dt> triangles = triangulation.getTriangles();
    		while (triangles.hasNext())
    		{
    			Triangle_dt t = triangles.next();
    			Point_dt p1 = t.p1();
    			Point_dt p2 = t.p2();
    			Point_dt p3 = t.p3();
				if (p1 != null && p2 != null && p3 != null)
				{
					int[] xPoints = new int[3];
					int[] yPoints = new int[3];
					

					xPoints[0] = (int)p1.x() * nPixelSize + nOffsetX;
					xPoints[1] = (int)p2.x() * nPixelSize + nOffsetX;
					xPoints[2] = (int)p3.x() * nPixelSize + nOffsetX;
					
					yPoints[0] = (int)p1.y() * nPixelSize + nOffsetY;
					yPoints[1] = (int)p2.y() * nPixelSize + nOffsetY;
					yPoints[2] = (int)p3.y() * nPixelSize + nOffsetY;
					
					g.drawPolygon(xPoints, yPoints, 3);
				}
    		}
		}
		
		public void DrawRandomEdgeSet(Graphics g)
		{
			double subset_radius = 5.0;
			ArrayList<GeoEdge> edges = new ArrayList<GeoEdge>();
			HashSet<Integer> visited = new HashSet<Integer>();
			
			String srcKey = DelaunayMapping.ConvertToStringKey(src);
			Integer srcIndex = triangulation.mapping.vertexIndices.get(srcKey);
			
//			Integer srcIndex = triangulation.mapping.vertexIndices.get_index(src.x(), src.y(), src.z());
			
			edges = triangulation.findEdgeSubset(src, src, srcIndex, visited, subset_radius);
			
			g.setColor(Color.ORANGE);
			for (GeoEdge ge: edges)
			{
				int x1 = (int)(ge.start.x() * nPixelSize) + nOffsetX;
				int y1 = (int)(ge.start.y() * nPixelSize) + nOffsetY;
				int x2 = (int)(ge.end.x() * nPixelSize) + nOffsetX;
				int y2 = (int)(ge.end.y() * nPixelSize) + nOffsetY;
				g.drawLine(x1, y1, x2, y2);
			}
			
		}
		
		public void DrawRandomEdgeVertexSet(Graphics g)
		{
			
			double subset_radius = 5.0;
			g.setColor(Color.BLUE);
			HashSet<Integer> visited = new HashSet<Integer>();
			ArrayList<GeoVertex> subset = triangulation.findEdgeVertexSubset(src, src, subset_radius, visited);
//			ArrayList<GeoVertex> subset = triangulation.findVertexSubset(src, src, subset_radius, visited);
			
			int r = 2; // vertex radius
			int x = 0;
			int y = 0;
			for (GeoVertex gv: subset)
			{
				x = (int)(gv.x() * nPixelSize) + nOffsetX;
				y = (int)(gv.y() * nPixelSize) + nOffsetY;
				g.drawOval(x-r, y-r, r*2, r*2);
			}
			
			x = (int)(src.x() * nPixelSize) + nOffsetX;
			y = (int)(src.y() * nPixelSize) + nOffsetY;
			g.setColor(Color.RED);
			g.drawOval(x-r, y-r, r*2, r*2);
			
			g.setColor(Color.GREEN);
			g.drawOval(x-(int)(subset_radius*nPixelSize), y-(int)(subset_radius*nPixelSize),
					(int)(subset_radius*nPixelSize)*2, (int)(subset_radius*nPixelSize)*2);
		}
		
		public void DrawNRing(Graphics g)
		{
			int n = 9;
			HashSet<Integer> visited = new HashSet<Integer>();
			ArrayList<ArrayList<GeoVertex>> ring = triangulation.findRingNeighbourhood(src, n, visited);
			
			int r = 2; // vertex radius
			int x = 0;
			int y = 0;

			x = (int)(src.x() * nPixelSize) + nOffsetX;
			y = (int)(src.y() * nPixelSize) + nOffsetY;
			g.setColor(Color.BLACK);
			g.fillOval(x-r, y-r, r*2, r*2);
			
			for (int i = 0; i < n; i++)
			{
				double wave = CLRConvert.ConvertToWaveLength(0, n, i);
				float[] rgb = CLRConvert.ConvertWaveLengthToRGB(wave);
				
				Color c = new Color(rgb[0]/255.0f, rgb[1]/255.0f, rgb[2]/255.0f);
				g.setColor(c);
				
				for (GeoVertex gv : ring.get(i))
				{
					x = (int)(gv.x() * nPixelSize) + nOffsetX;
					y = (int)(gv.y() * nPixelSize) + nOffsetY;
					g.drawOval(x-r, y-r, r*2, r*2);
				}
			}
		}
		
		public void paintComponent(Graphics g)
		{
			DrawTriangulation(g);
			
			if (src != null)
			{
				if (bShowNRing) DrawNRing(g);
				if (bShowEdgeVertexSet) DrawRandomEdgeVertexSet(g);
				if (bShowEdgeSet) DrawRandomEdgeSet(g);
			} 
		}
	}
	
	private DrawPane canvas;
	
	private JButton btnSelectRandomVertex;
	private JCheckBox cbShowNRing;
	private JCheckBox cbShowEdgeVertexSet;
	private JCheckBox cbShowEdgeSet;
	
	public Triangulation2DFrame(String title, SimpleTriangulation t)
	{
		super(title);
		
		setContentPane(canvas = new DrawPane());
		canvas.triangulation = t;
		
		// give simple keyboard operations to control the frame
		this.addKeyListener(new KeyListener()
		{

			public void keyTyped(KeyEvent e)
			{

			}
			
			public void keyReleased(KeyEvent e)
			{

			}
			
			@Override
			public void keyPressed(KeyEvent e)
			{
				int spd = 1;
				boolean bShift = (e.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) == KeyEvent.SHIFT_DOWN_MASK;
				if (bShift) spd = ACCEL_SPD;
				
				if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_A) canvas.nOffsetX -= (MOVE_SPD*spd);
				if (e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_D) canvas.nOffsetX += (MOVE_SPD*spd);
				if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_W) canvas.nOffsetY -= (MOVE_SPD*spd);
				if (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_S) canvas.nOffsetY += (MOVE_SPD*spd);
				
				if (e.getKeyCode() == KeyEvent.VK_Z) canvas.nPixelSize -= (ZOOM_SPD*spd);
				if (e.getKeyCode() == KeyEvent.VK_C) canvas.nPixelSize += (ZOOM_SPD*spd);
				
				if (e.getKeyCode() == KeyEvent.VK_1) canvas.bShowNRing = !canvas.bShowNRing;
				if (e.getKeyCode() == KeyEvent.VK_2) canvas.bShowEdgeVertexSet = !canvas.bShowEdgeVertexSet;
				if (e.getKeyCode() == KeyEvent.VK_3) canvas.bShowEdgeSet = !canvas.bShowEdgeSet;
				if (e.getKeyCode() == KeyEvent.VK_ENTER) canvas.selectedRandomVertex();
				
				repaint();
			}
		});
		
		setSize(canvas.getSize());
		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
	}
}
